/*****************************************************************************
 * $LastChangedDate: 2011-09-10 23:54:34 -0400 (Sat, 10 Sep 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Test SinCos().
 *          This is an x86-specific test.
 *//*
 * LEGAL:   COPYRIGHT (C) 2007 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#include "test.hh"
#include <utility>
#include "base/module.hh"
using namespace base;
#include "math/module.hh"
#include "math/funcs.hh"
using namespace math;

// Defined by base module.
//#if defined(_X86_)||defined(__x86__)||defined(i386)||defined(__i386__)||defined(__x86_64__)||defined(__x86_64)
//#   define CPU_X86 1
//#endif

typedef float TestRadian;
const double PI  = 3.14159265358979323846;
const double PI2 = 6.28318530717958647692;   // pi*2

/*****************************************************************************
 * SinCos_asm()
 *****************************************************************************/
#if !defined(__clang__) && defined(__GNUC__) && defined(CPU_X86)
template<typename FP>
std::pair<FP,FP> SinCos_asm( const TestRadian rad )
{
    std::pair<FP,FP> si_co;
    asm("fsincos" : "=t" (si_co.second), "=u" (si_co.first) : "0" (rad));
    return si_co;
}
#endif

/*****************************************************************************
 * SinCos_gnu()
 *****************************************************************************/
#if !defined(__clang__) && defined(__GNUC__) && _GLIBCXX_HAVE_SINCOS
template<typename FP>
std::pair<FP,FP> SinCos_gnu( const TestRadian rad )
{
    std::pair<double,double> si_co;  // must be specific in this case
    sincos( rad, &si_co.first, &si_co.second );  // GNU extension
    return std::make_pair<FP,FP>( si_co.first, si_co.second );
}
#endif

/*****************************************************************************
 * SinCos_sin_cos().
 *****************************************************************************/
template<typename FP>
std::pair<FP,FP> SinCos_sin_cos( const TestRadian rad )
{
    std::pair<FP,FP> si_co;
    si_co.first  = sin( rad );
    si_co.second = cos( rad );
    return si_co;
}

/*****************************************************************************
 * Test()
 *****************************************************************************/
bool
Test( const TestRadian rad )
{

#define CHECK_SI_CO( si_co )                                        \
{{                                                                  \
    if ( not FP_EQ( si_co.first,  std::sin(rad) ) ) return false;   \
    if ( not FP_EQ( si_co.second, std::cos(rad) ) ) return false;   \
}}

    COUT << "------ rad = " << rad << "--------------" << std::endl;
#if !defined(__clang__) && defined(__GNUC__) && defined(CPU_X86)
  {
    std::pair<float,float> si_co = SinCos_asm<float>( rad );
    COUT << "SinCos_asm_x86: " << si_co.first << " " << si_co.second << std::endl;
    CHECK_SI_CO( si_co );
  }
#endif

#if !defined(__clang__) && defined(__GNUC__) && _GLIBCXX_HAVE_SINCOS
  {
    std::pair<float,float> si_co = SinCos_gnu<float>( rad );
    COUT << "SinCos_gnu    : " << si_co.first << " " << si_co.second << std::endl;
    CHECK_SI_CO( si_co );
  }
#endif

  {
    std::pair<float,float> si_co = SinCos_sin_cos<float>( rad );
    COUT << "SinCos        : " << si_co.first << " " << si_co.second << std::endl;
    CHECK_SI_CO( si_co );
  }

    return true;
}

/*****************************************************************************
 * main()
 *****************************************************************************/
int main( int argc, char** argv )
{
    RUN_TEST( "SinCos", TEST_ERROR_MODE_ABORT,
              Test( 0.25 )
          and Test( 0.5 )
          and Test( M_PI )
          and Test( -M_PI )
          and Test( M_PI/2 )
          and Test( -M_PI/2 )
          and Test( M_PI/4 )
          and Test( -M_PI/4 ) )
}
